package com.zengqingfa.sharding.jdbc.demo.sharding.range;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Range;
import com.zengqingfa.sharding.jdbc.demo.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;

/**
 *
 * @fileName: ShardingTableRangeAlgorithm
 * @author: zengqf3
 * @date: 2021-3-28 21:40
 * @description: 分表算法
 */
@Slf4j
public class ShardingTableRangeAlgorithm implements RangeShardingAlgorithm<Timestamp> {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
            RangeShardingValue<Timestamp> rangeShardingValue) {
        log.info("availableTargetNames={}", JSON.toJSONString(availableTargetNames));
        log.info("rangeShardingValue={}", JSON.toJSONString(rangeShardingValue));
        final ArrayList<String> result = new ArrayList<>();
        final Range<Timestamp> range = rangeShardingValue.getValueRange();
        long startMillisecond = range.lowerEndpoint().getTime();
        long endMillisecond = range.upperEndpoint().getTime();

        // 起始年和结束年
        int startYear = Integer.parseInt(DateUtil.getYear(startMillisecond));
        int endYear = Integer.parseInt(DateUtil.getYear(endMillisecond));
        // 起始月和结束月
        int startMonth = Integer.parseInt(DateUtil.getMonth(startMillisecond));
        int endMonth = Integer.parseInt(DateUtil.getMonth(endMillisecond));

        int startYearJoinMonth = Integer.parseInt(DateUtil.getYearAndMonth(startMillisecond));
        int endYearJoinMonth = Integer.parseInt(DateUtil.getYearAndMonth(endMillisecond));
        return startYear == endYear ?
                theSameYear(startMonth, endMonth, availableTargetNames, result) :
                differentYear(startYear, endYear, startMonth, endMonth, startYearJoinMonth, endYearJoinMonth,
                        availableTargetNames, result);
    }


    /**
     * 同年，但可能不同月
     * @param startMonth
     * @param endMonth
     * @param availableTargetNames
     * @param result
     * @return
     */
    private Collection<String> theSameYear(int startMonth, int endMonth, Collection<String> availableTargetNames,
            ArrayList<String> result) {

        return startMonth == endMonth ?
                theSameMonth(startMonth, availableTargetNames, result) :
                differentMonth(startMonth, endMonth, availableTargetNames, result);
    }

    /**
     * 同年同月
     * @param startMonth
     * @param availableTargetNames
     * @param result
     * @return
     */
    private Collection<String> theSameMonth(int startMonth, Collection<String> availableTargetNames,
            ArrayList<String> result) {
        String startMonthStr = String.valueOf(startMonth);
        if (startMonthStr.length() == 1) {
            startMonthStr = "0" + startMonthStr;
        }
        for (String availableTargetName : availableTargetNames) {
            if (availableTargetName.endsWith(startMonthStr)) {
                log.info("availableTargetName={}",availableTargetName);
                result.add(availableTargetName);
            }
        }
        return result;
    }

    /**
     * 同年不同月
     * @param startMonth
     * @param endMonth
     * @param availableTargetNames
     * @param result
     * @return
     */
    private Collection<String> differentMonth(int startMonth, int endMonth, Collection<String> availableTargetNames,
            ArrayList<String> result) {
        for (String availableTargetName : availableTargetNames) {
            for (int i = startMonth; i <= endMonth; i++) {
                String monthStr = String.valueOf(i);
                if (monthStr.length() == 1) {
                    monthStr = "0" + monthStr;
                }

                if (availableTargetName.endsWith(monthStr)) {
                    log.info("availableTargetName={}",availableTargetName);
                    result.add(availableTargetName);
                }
            }
        }
        return result;
    }


    /**
     * 不同年，跨年，最少两个月，需要考虑跨两年以上的情况
     * @param startYear
     * @param endYear
     * @param startMonth
     * @param endMonth
     * @param startYearJoinMonth
     * @param endYearJoinMonth
     * @param availableTargetNames
     * @param result
     * @return
     */
    private Collection<String> differentYear(int startYear, int endYear, int startMonth, int endMonth,
            int startYearJoinMonth, int endYearJoinMonth, Collection<String> availableTargetNames,
            ArrayList<String> result) {

        return endYear - startYear == 1 ?
                twoYears(startYear, endYear, startMonth, endMonth, startYearJoinMonth, endYearJoinMonth,
                        availableTargetNames, result) :
                moreThanTwoYears(startYear, endYear, startMonth, endMonth, availableTargetNames, result);
    }


    /**
     * 两年
     * @param startYear
     * @param endYear
     * @param startMonth
     * @param endMonth
     * @param startYearJoinMonth
     * @param endYearJoinMonth
     * @param availableTargetNames
     * @param result
     * @return
     */
    private Collection<String> twoYears(int startYear, int endYear, int startMonth, int endMonth,
            int startYearJoinMonth, int endYearJoinMonth, Collection<String> availableTargetNames,
            ArrayList<String> result) {

        int endCondition;
        endCondition = Integer.parseInt(startYear + "12");
        for (int i = startYearJoinMonth; i <= endCondition; i++) {
            for (String availableTargetName : availableTargetNames) {
                // 如果多库此算法sharding会匹配两次，需要年份加月份来判断，只使用月份的话有问题
                if (availableTargetName.endsWith(String.valueOf(i))) {
                    result.add(availableTargetName);
                }
            }
        }

        endCondition = Integer.parseInt(endYear + "01");
        for (int i = endYearJoinMonth; i >= endCondition; i--) {
            for (String availableTargetName : availableTargetNames) {
                if (availableTargetName.endsWith(String.valueOf(i))) {
                    result.add(availableTargetName);
                }
            }
        }
        return result;
    }


    /**
     * 两年以上，如果数据量大的话不建议跨太多库
     * @param startYear
     * @param endYear
     * @param startMonth
     * @param endMonth
     * @param availableTargetNames
     * @param result
     * @return
     */
    private Collection<String> moreThanTwoYears(int startYear, int endYear, int startMonth, int endMonth,
            Collection<String> availableTargetNames, ArrayList<String> result) {
        return null;
    }
}
